#ifndef GST_CU_AREA_LIMITER_HPP__
#define GST_CU_AREA_LIMITER_HPP__

#include "Client/ClientUtils/Network/NetworkInterfaceStructs.h"
#include "GSTCore/GST_Types.h"
#include "GSTenums.h"
#include "Geometry/SRS.h"
#include "buildspec.h"

#include <boost/optional.hpp>

#include <string>

namespace GST
{
namespace ClientUtils
{
// A geometry file reference.
class GST_API_EXPORT GeometryFile
{
public:
	GeometryFile(const std::string &filePath,
				 const std::string &mainFileName,
				 Geometry::RequestFormats requestFormat,
				 Geometry::SRSPtr srs);
	const std::string &filePath() const;
	const std::string &mainFileName() const;
	Geometry::RequestFormats fileFormat() const;
	Geometry::SRSPtr srs() const;

private:
	// The path to the directory of the geometry.
	std::string m_filePath;
	// The file name of the geometry (relative to file path).
	std::string m_mainFileName;
	// The file format of the geometry.
	Geometry::RequestFormats m_fileFormat;
	// The srs of the geometry.
	Geometry::SRSPtr m_srs;
};

// A value range.
class GST_API_EXPORT Range
{
public:
	Range(double min, double max);
	double min() const;
	double max() const;

private:
	// The min value. Unlimited if not set.
	double m_min;
	// The max value. Unlimited if not set.
	double m_max;
};

class GST_API_EXPORT Polygon
{
public:
	Polygon(const std::vector<std::vector<Point3>> &polygons,
			Geometry::SRSPtr srs);
	const std::vector<std::vector<Point3>> &polygons() const;
	Geometry::SRSPtr srs() const;

private:
	std::vector<std::vector<Point3>> m_polygons;
	Geometry::SRSPtr m_srs;
};

// A polygon selection.
//
// The polygon data is read from a geometry file or from an existing feature.
class GST_API_EXPORT PolygonSelection
{
public:
	// Creates polygon selection that is read from a geometry file.
	PolygonSelection(const GeometryFile &geometryFile,
					 const boost::optional<Range> &zRange);
	// Creates polygon selection that is read from an existing features.
	PolygonSelection(const FeatureDesc &feature,
					 const boost::optional<Range> &zRange);
	// Creates polygon selection from a polygon.
	PolygonSelection(const Polygon &polygon,
					 const boost::optional<Range> &zRange);
	// Is initialized, if created with a geometry file.
	const boost::optional<GeometryFile> &geometryFile() const;
	// Is initialized, if created with a feature.
	const boost::optional<FeatureDesc> &feature() const;
	// Is initialized, if created with polygon.
	const boost::optional<Polygon> &polygon() const;
	// Is initialized, if created with a z-range.
	const boost::optional<Range> &zRange() const;
	Geometry::SRSPtr srs() const;

private:
	boost::optional<GeometryFile> m_geometryFile;
	boost::optional<FeatureDesc> m_feature;
	boost::optional<Polygon> m_polygon;
	boost::optional<Range> m_zRange;
};

// A bounding box with srs.
//
// Combines box and srs.
class GST_API_EXPORT BoundingBox
{
public:
	BoundingBox(const GSTCore::GSTbox &box, Geometry::SRSPtr srs)
		: m_box(box), m_srs(Geometry::SRS::SafePtr(srs))
	{
	}
	const GSTCore::GSTbox &box() const
	{
		return m_box;
	}
	Geometry::SRSPtr srs() const
	{
		return m_srs;
	}

private:
	GSTCore::GSTbox m_box;
	Geometry::SRSPtr m_srs;
};

// The area in which a geometry should be loaded.
class GST_API_EXPORT Area
{
public:
	// Creates an area with a polygon selection.
	Area(const PolygonSelection &polygonSelection);
	// Creates an area with a bounding box.
	Area(const BoundingBox &boundingBox);
	// Is initialized, if created with a polygon selection.
	const boost::optional<PolygonSelection> &polygonSelection() const;
	// Is initialized, if created with a bounding box.
	const boost::optional<BoundingBox> &boundingBox() const;

private:
	boost::optional<PolygonSelection> m_polygonSelection;
	boost::optional<BoundingBox> m_boundingBox;
};

// The mode in which a geometry should be loaded.
enum class GeometryLoadMode
{
	// The full geometry should be loaded, which may be parts inside and outside
	// of the requested area.
	Full = 0,
	// Only parts of the geometry inside of the requested area will be loaded.
	Partial = 1,
};

// The threshold a geometry has to clear to be loaded.
enum class GeometryLoadThreshold
{
	// Geometries have to be fully included in the requested area
	Contained = 0,
	// Geometries may be partially included in the requested area
	Intersected = 1,
};

// Combines area, load mode and load threshold.
class GST_API_EXPORT AreaLimiter
{
public:
	AreaLimiter(const Area &area,
				GeometryLoadMode geometryLoadMode,
				GeometryLoadThreshold geometryLoadThreshold,
				const boost::optional<double> &safeAreaWidth);
	const Area &area() const;
	GeometryLoadMode geometryLoadMode() const;
	GeometryLoadThreshold geometryLoadThreshold() const;
	const boost::optional<double> &safeAreaWidth() const;

private:
	Area m_area;
	GeometryLoadMode m_geometryLoadMode;
	GeometryLoadThreshold m_geometryLoadThreshold;
	boost::optional<double> m_safeAreaWidth;
};
} // namespace ClientUtils
} // namespace GST

#endif // GST_CU_AREA_LIMITER_HPP__
